package com.jrx.anytxn.customer.service.impl;

import com.alibaba.fastjson.JSON;
import com.jrx.anytxn.common.constant.Constants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.StringUtils;
import com.jrx.anytxn.customer.constant.CouponConstant;
import com.jrx.anytxn.customer.constant.CustomerConstant;
import com.jrx.anytxn.customer.dto.coupon.CouponReq;
import com.jrx.anytxn.customer.dto.coupon.RightsExchangeLogRes;
import com.jrx.anytxn.customer.dto.coupon.SendYdCouponReq;
import com.jrx.anytxn.customer.dto.customer.CustomerFirstLevelRes;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.dto.customer.CustomerSecondLevelRes;
import com.jrx.anytxn.customer.dto.rights.CustRightRes;
import com.jrx.anytxn.customer.entity.CmCustomerRightInfo;
import com.jrx.anytxn.customer.entity.TlCouponDetail;
import com.jrx.anytxn.customer.entity.TlRightsExchangeLog;
import com.jrx.anytxn.customer.mapper.base.CmCustomerRightInfoMapper;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerRightInfoMapper;
import com.jrx.anytxn.customer.service.ICouponInputService;
import com.jrx.anytxn.customer.service.ICouponMeaasgeService;
import com.jrx.anytxn.customer.service.ICustomerRightService;
import com.jrx.anytxn.customer.service.ICustomerService;
import com.jrx.anytxn.customer.service.IRightsExchangeLogService;
import com.jrx.anytxn.customer.service.ITlCouponDetailService;
import com.jrx.anytxn.customer.util.Md5Utils;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.feign.gateway.bean.ApiResponse;
import com.jrx.anytxn.feign.gateway.bean.Card;
import com.jrx.anytxn.feign.gateway.request.CouponRequest05;
import com.jrx.anytxn.feign.gateway.request.CouponRequest06;
import com.jrx.anytxn.feign.gateway.response.CouponResponse05;
import com.jrx.anytxn.feign.gateway.response.CouponResponse06;
import com.jrx.anytxn.feign.gateway.service.IOuterGatewayService;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.constant.ErrorMsgConstant;
import com.jrx.anytxn.param.dto.rights.CardCouponDetailRes;
import com.jrx.anytxn.param.dto.rights.PrRightsQualificationRes;
import com.jrx.anytxn.param.dto.system.CodeTableTypeRes;
import com.jrx.anytxn.param.entity.PrActivityInfo;
import com.jrx.anytxn.param.entity.PrCardRollDetailInfo;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrRightsAndInterests;
import com.jrx.anytxn.param.entity.PrRightsQualification;
import com.jrx.anytxn.param.enums.RightsAndActivityLimitTimeEnum;
import com.jrx.anytxn.param.enums.RightsCycleInActivityEnum;
import com.jrx.anytxn.param.enums.RightsEffectivePeriodInActivityEnum;
import com.jrx.anytxn.param.enums.RightsTypeEnum;
import com.jrx.anytxn.param.mapper.ext.ExtPrRightsAndInterestsMapper;
import com.jrx.anytxn.param.service.cardRoll.ICardRollService;
import com.jrx.anytxn.param.service.rights.IPrActivityInfoService;
import com.jrx.anytxn.param.service.rights.IPrRightsAndInterestsService;
import com.jrx.anytxn.param.service.rights.IPrRightsQualificationService;
import com.jrx.anytxn.param.service.system.ICodeTableTypeService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.redisson.service.RedissonLockService;
import com.jrx.anytxn.redisson.util.LockType;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/***
 * 权益服务实现类
 */
@Service
public class CouponInputServiceImpl implements ICouponInputService {

    private static final Logger logger = LoggerFactory.getLogger(CouponInputServiceImpl.class);

    private final String COUPON_LOCK_TEMPLATE = "COUPON_TENANT_%s_ACTIVITY_%s";

    // 权益资格兑换用户锁
    private final String COUPON_EXCHANGE_LOCK_USER_KEY = "COUPON_EXG_LCK_UID_%s";
    // 权益资格兑换权益锁
    private final String COUPON_EXCHANGE_LOCK_RIGHTS_KEY = "COUPON_EXG_LCK_RID_%s";

    private final String REDIS_EXCHANGE_USER_KEY = "anytxn_coupon_exg_uid_%s";

    private final String EXCHANGE_OUT_RIGHT_SWITCH = "EX_OUT_RIGHT_SWITCH";

    @Autowired
    private IPrRightsAndInterestsService prRightsAndInterestsService;
    @Autowired
    private ExtPrRightsAndInterestsMapper extPrRightsAndInterestsMapper;
    @Autowired
    private IOrganizationTableService organizationTableService;
    @Autowired
    private ISegmentService iSegmentService;
    @Resource
    private ExtCmCustomerRightInfoMapper extCmCustomerRightInfoMapper;

    @Autowired
    private ICustomerService customerService;
    @Autowired
    private ICouponMeaasgeService couponMessageService;
    @Autowired
    private ITlCouponDetailService tlCouponDetailService;
    @Autowired
    private CmCustomerRightInfoMapper cmCustomerRightInfoMapper;
    @Autowired
    private IOuterGatewayService outerGatewayService;

    @Autowired
    private ICustomerRightService customerRightService;

    @Autowired
    private IPrRightsQualificationService prRightsQualificationService;
    @Autowired
    private IRightsExchangeLogService rightsExchangeLogService;
    @Autowired
    private IPrActivityInfoService activityInfoService;
    @Autowired
    private RedissonLockService redissonLockService;
    @Lazy
    @Autowired
    private IUserApiService userApiService;

    @Autowired
    protected StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ICodeTableTypeService iCodeTableTypeService;

    @Autowired
    private ICardRollService iCardRollService;

    @Override
    public void couponInput(List<Map<String, Object>> list, String tenantId, String userName, String fileName)
            throws TxnException {
        List<CmCustomerRightInfo> infoList = new ArrayList<>();
        List<TlCouponDetail> detailList = new ArrayList<>();

        Long sumId;
        try {
            sumId = iSegmentService.getId(CustomerConstant.TL_COUPON_SUMMARY_INFO);
        } catch (SegmentException e) {
            logger.error("主键生成错误", e);
            throw new TxnException(ErrorMsgConstant.ERROR_ID);
        }

        for (Map<String, Object> m : list) {
            String customerId = m.get("customerId").toString();
            String rightsId = m.get("rightsId").toString();
            String channel = m.get("channel").toString();

            if ("customerId".equals(customerId)) {
                // 如果是第一行跳过
                continue;
            }

            logger.info("文件属性：customerId:{},rightsId:{},channel:{}", customerId, rightsId, channel);
            // 获取客户信息
            CustomerRes customerRes =
                    customerService.getCustomerByChannelAndCustomerIdAndTensnId(channel, customerId, tenantId);

            if (null == customerRes) {
                logger.error("客户数据不存在");
                throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            }

            // 获取权益参数ID
            PrRightsAndInterests rightsAndInterests = prRightsAndInterestsService.getByRightsId(tenantId, rightsId);
            if (null == rightsAndInterests) {
                logger.error("优惠券不存在");
                throw new TxnBizException(TxnRespCode.NO_DATA.getCode(), TxnRespCode.NO_DATA.getUserTips());
            }

            if (CustomerConstant.RIGHTS_STATUS_0.equals(rightsAndInterests.getRightsStatus())) {
                logger.error("优惠券已失效");
                throw new TxnBizException(TxnRespCode.PARAM_ERROR.getCode(), TxnRespCode.PARAM_ERROR.getUserTips());
            }

            // 获取业务日期
            PrOrganizationTable org =
                    organizationTableService.findByOrId(rightsAndInterests.getOrganizationId(), tenantId);
            Date businessDate = org.getNextProcessingDate();

            CmCustomerRightInfo cmCustomerRightInfo = BeanMapping.copy(rightsAndInterests, CmCustomerRightInfo.class);
            try {
                cmCustomerRightInfo.setId(iSegmentService.getId(CustomerConstant.CM_CUSTOMER_RIGHT_INFO));
            } catch (SegmentException e) {
                logger.error("主键生成错误", e);
                throw new TxnException(ErrorMsgConstant.ERROR_ID);
            }
            cmCustomerRightInfo.setChannel(channel);
            cmCustomerRightInfo.setCustomerId(customerId);
            cmCustomerRightInfo.setEffectDate(businessDate);
            Date expiresDate = DateUtils.getDate(businessDate, rightsAndInterests.getValidDays() - 1);
            cmCustomerRightInfo.setExpiresDate(expiresDate);
            cmCustomerRightInfo.setLoanId(" ");
            cmCustomerRightInfo.setRightsStatus(CustomerConstant.RIGHTS_STATUS_0);
            cmCustomerRightInfo.setUpdateBy(userName);
            cmCustomerRightInfo.setRecordVersionNumber(Constant.VERSION_NUMBER);

            TlCouponDetail detail =
                    couponMessageService.buildCouponDetail(cmCustomerRightInfo, customerRes, rightsAndInterests, sumId);
            detailList.add(detail);
            infoList.add(cmCustomerRightInfo);
        }
        logger.info("写入数据，数据量:{}", infoList.size());
        extCmCustomerRightInfoMapper.batchInsertSelective(infoList);
        // 写入汇总信息
        couponMessageService.buildCouponSummary(sumId, tenantId, infoList.get(0).getOrganizationId(), fileName,
                infoList.size(), CustomerConstant.SUCCESS, userName);
        // 写入明细信息
        tlCouponDetailService.addCouponDetail(detailList);
    }

    /**
     * 权益下发 区分活动下发（检验活动） 和 非活动下单（不校验活动）
     *
     * @param couponReq 权益请求对象
     * @throws TxnBizException
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public List<TlCouponDetail> grantCoupon(CouponReq couponReq) throws TxnException {
        String type = couponReq.getType();
        if (StringUtils.isBlank(type)) {
            logger.error("权益下发权益类型不能为空。");
            throw new TxnBizException(TxnRespCode.COUPON_TYPE_MISSING);
        }

        String userId = couponReq.getUserId();
        String channel = couponReq.getChannel();
        String tenantId = couponReq.getTenantId();
        String rightsId = couponReq.getRightsId();
        String activityId = couponReq.getActivityId();
        String productCode = couponReq.getProductCode();
        Integer rightsQuantity = couponReq.getBuyQuantity();
        String customerId = couponReq.getCustomerId();
        Date effectiveDate = couponReq.getEffectiveDate();
        Date expireDate = couponReq.getExpireDate();
        String subChannel = couponReq.getSubChannel();

        PrActivityInfo activityInfo = null;
        String activityType = null;
        if (checkActivityId(activityId)) {
            activityInfo = activityInfoService.checkActivity(tenantId, activityId);
            rightsQuantity = activityInfo.getRightsQuantity();
            activityType = activityInfo.getActivityType();
            rightsId = activityInfo.getRightsId();
        } else {
            if (rightsQuantity == null) {
                rightsQuantity = 1;
            }
        }

        //设置type
        type = setCouponType(activityType, type);

        CustomerRes customerRes = null;
        if (StringUtils.isNotBlank(userId)) {
            customerRes = customerService.getCustomerByChannelAndUserId(channel, userId, tenantId);
        } else if (StringUtils.isNotBlank(customerId)) {
            customerRes = customerService.getCustomerByChannelAndCustomerIdAndTensnId(channel, customerId, tenantId);
        }

        if (customerRes == null) {
            logger.error("根据channel、userid、tenantId 查询一级表，二级表信息，查无记录");
            throw new TxnBizException(TxnRespCode.NO_DATA);
        }
        CustomerFirstLevelRes customerFirstLevelRes = customerRes.getFirstLevel();
        CustomerSecondLevelRes customerSecondLevelRes = customerRes.getSecondLevel();
        customerId = customerSecondLevelRes.getCustomerId();

        // 生成用户权益实例
        CmCustomerRightInfo cmCustomerRightInfo = generateCmCustomerRightInfo(couponReq, customerId, rightsQuantity);

        // 租户下，活动权益领取时的分布式锁支持
        String lockKey = String.format(COUPON_LOCK_TEMPLATE, couponReq.getTenantId(), couponReq.getActivityId());
        RLock rlock = redissonLockService.getRLock(lockKey, LockType.REENTRANT_LOCK);
        try {
            boolean lockFirst = rlock.tryLock(0, 10, TimeUnit.SECONDS);
            logger.info("当前userId:{} 尝试拿锁，{}", userId, lockFirst);
            if (!lockFirst) {
                throw new TxnBizException(TxnRespCode.TRY_LOCK_FAIL);
            }
            logger.info("userId:{} 第一次拿到锁", userId);

            // 优惠券下发
            if (type.equals(CouponConstant.COUPON_TYPE_COUPON)) {
                //用来统计rightsId数组的元素种类即对应数量
                LinkedHashSet<String> rightsLinkedHashSet = new LinkedHashSet<>();
                Map<String, Integer> rightsMap = new HashMap<>();
                Map<String, PrRightsAndInterests> rightsInfoMap = new HashMap<>();
                List<CmCustomerRightInfo> cmCustomerRightInfoList = new ArrayList<>(16);

                if (StringUtils.isBlank(rightsId)) {
                    return null;
                }
                // 验证权益参数信息,默认使用第一条权益id
                String[] rightsIdArr = rightsId.split(",");
                PrRightsAndInterests rightsAndInterests = prRightsAndInterestsService.checkRightsAndInterests(tenantId, rightsIdArr[0], productCode);
                rightsInfoMap.put(rightsAndInterests.getRightsId(), rightsAndInterests);

                // 默认将生效日期、失效日期 设置为 权益参数表中对应的生效日期、失效日期
                cmCustomerRightInfo.setEffectDate(
                        DateUtils.parseDate(rightsAndInterests.getEffectiveDate(), DateUtils.DATE_PATTERN));
                cmCustomerRightInfo.setExpiresDate(
                        DateUtils.parseDate(rightsAndInterests.getExpirationDate(), DateUtils.DATE_PATTERN));

                //设置子渠道
                if (StringUtils.isBlank(subChannel)) {
                    subChannel = "channel01";
                }
                cmCustomerRightInfo.setSubChannel(subChannel);

                if (checkActivityId(activityId)) {
                    // 设置期限
                    String rightsEffectivePeriod = activityInfo.getRightsEffectivePeriod();
                    if (RightsEffectivePeriodInActivityEnum.FRIDAY_TO_SUNDAY.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate =
                                DateUtils.parseDate(DateUtils.getDayOfWeek(Calendar.FRIDAY), DateUtils.DATE_PATTERN);
                        expireDate =
                                DateUtils.parseDate(DateUtils.getDayOfWeek(Calendar.SUNDAY), DateUtils.DATE_PATTERN);
                    }
                    if (RightsEffectivePeriodInActivityEnum.MONTHLY.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate = DateUtils.getMonthStartTime(new Date());
                        expireDate = DateUtils.getMonthEndTime(new Date());
                    }
                    if (RightsEffectivePeriodInActivityEnum.MONTHSLY.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate = DateUtils.getMonthStartTime(new Date());
                        expireDate = DateUtils.getMonthEndTime(new Date());
                    }
                    if (RightsEffectivePeriodInActivityEnum.ONEYEAR.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate = new Date();
                        //一年后的日期
                        Date afterOneYearDate = DateUtils.addYear(new Date(), 1);
                        //过期时间
                        expireDate = DateUtils.addDay(afterOneYearDate, -1);
                    }
                    if (null != effectiveDate && null != expireDate) {
                        cmCustomerRightInfo.setEffectDate(effectiveDate);
                        cmCustomerRightInfo.setExpiresDate(expireDate);
                    }
                    PrRightsAndInterests rightsAndInterestsTemp = null;

                    String[] rightsIds = rightsId.split(",");

                    for (int i = 0; i < rightsIds.length; i++) {
                        rightsLinkedHashSet.add(rightsIds[i]);//有序不重复的链表，用来存放权益ID
                        if (rightsMap.containsKey(rightsIds[i])) {
                            rightsMap.put(rightsIds[i], rightsMap.get(rightsIds[i]) + 1);
                        } else {
                            if (rightsIds.length > 1) {
                                rightsMap.put(rightsIds[i], 1);
                            } else {//只有一个rightsId的情况，直接扣减活动表里配置的下发数量
                                rightsMap.put(rightsIds[i], rightsQuantity);
                            }
                        }
                    }

                    //校验活动下发的权益数量是否超过库存
                    checkRightsStoreQuantity(rightsMap, tenantId);

                    for (int i = 0; i < rightsIds.length; i++) {
                        /**
                         * 判断是否已存在下发的权益,存在则提示已下发，否则正常下发
                         */
                        checkActivityGrantedRights(customerId, activityInfo, rightsIds[i], tenantId);

                        rightsAndInterestsTemp = prRightsAndInterestsService.checkRightsAndInterests(tenantId, rightsIdArr[i], productCode);
                        if (!rightsInfoMap.containsKey(rightsAndInterestsTemp.getRightsId())) {
                            rightsInfoMap.put(rightsAndInterestsTemp.getRightsId(), rightsAndInterestsTemp);
                        }

                        //组装客户权益信息
                        setcmCustomerRightIfoProp(cmCustomerRightInfo, rightsAndInterestsTemp, rightsIds[i], tenantId);

                        int buyQuantity = activityInfo.getRightsQuantity();
                        if (RightsEffectivePeriodInActivityEnum.MONTHSLY.getCode().equals(activityInfo.getRightsEffectivePeriod())) {
                            logger.info("---处理连月发放类型的活动---");
                            //有配置多个rightsId
                            if (rightsIds.length > 1) {
                                if (buyQuantity != rightsIds.length) {
                                    throw new TxnBizException(TxnRespCode.ACTIVITY_RIGHTS_QUANTITY_MISMATCHED);
                                }
                                setMonthslyTypeProp(i, cmCustomerRightInfo, cmCustomerRightInfoList);
                            } else {//只配置了一个rightsId
                                for (int j = 0; j < buyQuantity; j++) {
                                    setMonthslyTypeProp(j, cmCustomerRightInfo, cmCustomerRightInfoList);
                                }
                            }
                        } else {

                            if (rightsIds.length > 1) {
                                CmCustomerRightInfo tmp = new CmCustomerRightInfo();
                                BeanUtils.copyProperties(cmCustomerRightInfo, tmp);
                                logger.info("---有多个rightsId---");
                                if (buyQuantity != rightsIds.length) {
                                    throw new TxnBizException(TxnRespCode.ACTIVITY_RIGHTS_QUANTITY_MISMATCHED);
                                }
                                if (i > 0){
                                    try {
                                        tmp.setId(iSegmentService.getId(CustomerConstant.CM_CUSTOMER_RIGHT_INFO));
                                    } catch (SegmentException e) {
                                        throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
                                    }
                                }
                                cmCustomerRightInfoList.add(tmp);
                            } else {
                                logger.info("---只有一个rightsId---");
                                for (int j = 0; j < buyQuantity; j++) {
                                    CmCustomerRightInfo tmp = new CmCustomerRightInfo();
                                    BeanUtils.copyProperties(cmCustomerRightInfo, tmp);
                                    if (j > 0) {
                                        try {
                                            tmp.setId(iSegmentService.getId(CustomerConstant.CM_CUSTOMER_RIGHT_INFO));
                                        } catch (SegmentException e) {
                                            throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
                                        }
                                    }
                                    logger.info("当前客户权益id："+tmp.getId());
                                    cmCustomerRightInfoList.add(tmp);
                                }
                            }
                        }
                    }
                }

                //非活动下发权益
                if (StringUtils.isBlank(activityId)) {
                    //组装客户权益信息
                    setcmCustomerRightIfoProp(cmCustomerRightInfo, rightsAndInterests, rightsId, tenantId);
                    cmCustomerRightInfoList.add(cmCustomerRightInfo);
                }


                List<TlCouponDetail> couponDetailList = generateCouponDetail(cmCustomerRightInfoList,
                        rightsAndInterests.getRightsName(), rightsAndInterests.getRightsType(), customerFirstLevelRes,
                        customerSecondLevelRes.getMobile(), couponReq);

                // 再次尝试拿锁
                boolean lockAgain = rlock.tryLock(0, 10, TimeUnit.SECONDS);
                if (!lockAgain) {
                    logger.info("userId:{} 更新权益库存前二次拿锁失败，请重新再试", userId);
                    throw new TxnBizException(TxnRespCode.TRY_LOCK_AGAIN_FAIL);
                }

                logger.info("userId:{} 更新权益库存", userId);
                logger.info("客户权益信息集合数据："+JSON.toJSONString(cmCustomerRightInfoList));
                extCmCustomerRightInfoMapper.batchInsertSelective(cmCustomerRightInfoList);
                tlCouponDetailService.insertBatch(couponDetailList);
                // 权益库存更新
                for (int i = 0; i < rightsLinkedHashSet.size(); i++) {
                    Integer count = rightsMap.get(rightsLinkedHashSet.toArray()[i]);
                    PrRightsAndInterests prRightsAndInterests = rightsInfoMap.get(rightsLinkedHashSet.toArray()[i]);

                    prRightsAndInterests.setStoreQuantity(prRightsAndInterests.getStoreQuantity() - count);
                    prRightsAndInterests.setUpdateTime(new Date());
                    prRightsAndInterests.setUpdateBy(customerId);
                    extPrRightsAndInterestsMapper.updateByRightsIdAndTenantId(prRightsAndInterests);
                }


                // 更新活动库存
                if (checkActivityId(activityId)) {
                    activityInfo.setStoreQuantity(activityInfo.getStoreQuantity() - 1);
                    activityInfo.setUpdateTime(new Date());
                    activityInfo.setUpdateBy(customerId);
                    activityInfoService.updateByPrimaryKeySelective(activityInfo);
                }

                return couponDetailList;
            }
            // 权益资格类
            if (type.equals(CouponConstant.COUPON_TYPE_QUALIFICATION)) {
                String name = null;
                if (checkActivityId(activityId)) {
                    checkActivityGrantedRights(customerId, activityInfo, activityInfo.getRightsId(), tenantId);
                    // 设置期限
                    String rightsEffectivePeriod = activityInfo.getRightsEffectivePeriod();
                    if (RightsEffectivePeriodInActivityEnum.FRIDAY_TO_SUNDAY.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate =
                                DateUtils.parseDate(DateUtils.getDayOfWeek(Calendar.FRIDAY), DateUtils.DATE_PATTERN);
                        expireDate =
                                DateUtils.parseDate(DateUtils.getDayOfWeek(Calendar.SUNDAY), DateUtils.DATE_PATTERN);
                    }
                    if (RightsEffectivePeriodInActivityEnum.MONTHLY.getCode().equals(rightsEffectivePeriod)) {
                        effectiveDate = DateUtils.getMonthStartTime(new Date());
                        expireDate = DateUtils.getMonthEndTime(new Date());
                    }
                    cmCustomerRightInfo.setEffectDate(effectiveDate);
                    cmCustomerRightInfo.setExpiresDate(expireDate);
                    cmCustomerRightInfo.setRightsQualificationId(activityInfo.getRightsId());
                    name = activityInfo.getName();
                    cmCustomerRightInfo.setRightsId(activityInfo.getRightsId());
                } else {
                    String rightsQualificationId = couponReq.getRightsId();
                    List<PrRightsQualificationRes> rightsQualificationList =
                            prRightsQualificationService.checkQualification(tenantId, rightsQualificationId, productCode);

                    if (rightsQualificationList.size() == 1) {
                        PrRightsQualificationRes interestsRes = rightsQualificationList.get(0);
                        if (effectiveDate == null) {
                            effectiveDate = DateUtils.parseDate(interestsRes.getEffectDate(), DateUtils.DATE_PATTERN);
                        }
                        if (expireDate == null) {
                            expireDate = DateUtils.parseDate(interestsRes.getExpireDate(), DateUtils.DATE_PATTERN);
                        }
                        name = interestsRes.getName();
                    } else {
                        name = rightsQualificationList.stream().map(PrRightsQualificationRes::getName)
                                .collect(Collectors.joining(",")) + "选一";
                    }

                    cmCustomerRightInfo.setEffectDate(effectiveDate);
                    cmCustomerRightInfo.setExpiresDate(expireDate);
                    cmCustomerRightInfo.setRightsQualificationId(rightsQualificationId);
                    cmCustomerRightInfo.setRightsId(rightsQualificationId);
                }

                cmCustomerRightInfo.setRightsName(name);
                cmCustomerRightInfo.setOrganizationId(Constant.DEFAULT_ORG_NUMBER);
                cmCustomerRightInfo.setCycleUse(CouponConstant.CYCLE_USE_N);
                cmCustomerRightInfo.setRightsType(RightsTypeEnum.QUALIFICATION.getCode());
                cmCustomerRightInfo.setAllowProducts(couponReq.getProductCode());

                List<CmCustomerRightInfo> cmCustomerRightInfoList = new ArrayList<>(4);
                cmCustomerRightInfoList.add(cmCustomerRightInfo);

                List<TlCouponDetail> couponDetail =
                        generateCouponDetail(cmCustomerRightInfoList, name, RightsTypeEnum.QUALIFICATION.getCode(),
                                customerFirstLevelRes, customerSecondLevelRes.getMobile(), couponReq);

                boolean lockAgain = rlock.tryLock(0, 10, TimeUnit.SECONDS);
                if (!lockAgain) {
                    logger.info("userId:{} 更新权益资格库存前二次拿锁失败，请重新再试", userId);
                    throw new TxnBizException(TxnRespCode.TRY_LOCK_AGAIN_FAIL);
                }

                logger.info("userId:{} 更新权益资格库", userId);

                tlCouponDetailService.insertBatch(couponDetail);

                cmCustomerRightInfo.setRightsId(null);
                cmCustomerRightInfoMapper.insert(cmCustomerRightInfo);

                // 更新活动库存
                if (checkActivityId(activityId)) {
                    activityInfo.setStoreQuantity(activityInfo.getStoreQuantity() - 1);
                    activityInfo.setUpdateTime(new Date());
                    activityInfo.setUpdateBy(customerId);
                    activityInfoService.updateByPrimaryKeySelective(activityInfo);
                }
                return couponDetail;
            }
            //易点平台发券类
            if (type.equals(CouponConstant.COUPON_TYPE_THIRD_COUPON)) {
                logger.info("---通知易点平台发券---");
                logger.info("---组装易点请求参数---");
                Date activateTime = new Date();
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                SendYdCouponReq sendYdCouponReq = new SendYdCouponReq();
                sendYdCouponReq.setTenantId(tenantId);
                sendYdCouponReq.setChannelNo(subChannel);
                sendYdCouponReq.setPhone(customerSecondLevelRes.getMobile());
                sendYdCouponReq.setProductId(activityInfo.getRightsId());
                sendYdCouponReq.setType(CouponConstant.TYPE_WXLJJ);
                sendYdCouponReq.setActivateTime(df.format(activateTime));
                ApiResponse apiResponse = userApiService.sendYdCoupon(JSON.toJSONString(sendYdCouponReq));

                if (TxnRespCode.SUCCESS.getCode() != apiResponse.getCode()) {
                    throw new TxnBizException(apiResponse.getCode(), apiResponse.getMessage());
                }

                // 写权益资格兑换日志表
                saveRightsExchangeLogByYd(sendYdCouponReq, customerRes);
                return null;
            }
            //权益平台下单类
            if (type.equals(CouponConstant.COUPON_TYPE_COUPON_BOSS)) {
                //校验该账号是否下单过
                List<RightsExchangeLogRes> rightsExchangeLogResList = rightsExchangeLogService.listByUserIdAndTenantIdAndChannel(couponReq.getUserId(), tenantId, channel);
                if (!rightsExchangeLogResList.isEmpty()) {
                    throw new TxnBizException(TxnRespCode.COUPON_IN_ACTIVITY_ALREADY_EXISTS);
                }
                logger.info("---权益平台下单中---");
                String rightsIds = activityInfo.getRightsId();
                PrRightsAndInterests rightsAndInterests = prRightsAndInterestsService.checkRightsAndInterests(tenantId, rightsIds, productCode);

                //权益平台下单
                String sceneOrderId = System.currentTimeMillis() + "";
                String createCouponJson = generateCreateOderReq(activityInfo, customerSecondLevelRes.getMobile(), sceneOrderId);
                ApiResponse<CouponResponse05> couponResponse05ApiResponse = outerGatewayService.order(createCouponJson);

                logger.info("权益兑换，请求：{}，响应：{}", createCouponJson, JSON.toJSONString(couponResponse05ApiResponse));

                if (TxnRespCode.SUCCESS.getCode() != couponResponse05ApiResponse.getCode()) {
                    throw new TxnBizException(couponResponse05ApiResponse.getCode(), couponResponse05ApiResponse.getMessage());
                }
                CouponResponse05 couponResponse05 = couponResponse05ApiResponse.getData();
                CouponReq couponReq1 = couponReq;
                couponReq1.setBuyQuantity(activityInfo.getRightsQuantity());
                couponReq1.setChargeAccount(customerSecondLevelRes.getMobile());
                if (couponResponse05 == null || CustomerConstant.FAIL.equals(couponResponse05.getDealState())) {
                    // 下单失败 增加权益下发失败记录，后期做补偿处理 // TODO
                    saveRightsExchangeLogOnCreatingFail(couponResponse05, couponReq1, customerRes, rightsAndInterests);
                    throw new TxnBizException(Integer.valueOf(couponResponse05.getBusCode()), couponResponse05.getBusMsg());
                }
                logger.info("---权益平台下单成功---");
                // 写权益资格兑换日志表
                saveRightsExchangeLogOnCreatingSuccess(couponResponse05, couponReq1, customerRes,
                        rightsAndInterests, sceneOrderId);

            }
        } catch (TxnBizException bizException) {
            throw bizException;
        } catch (Exception e) {
            throw new TxnBizException(TxnRespCode.ERROR.getCode(), e.getMessage());
        } finally {
            if (rlock.isLocked() && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }

        return null;
    }


    /**
     * 生成发放明细实体
     *
     * @param cmCustomerRightInfoList
     * @param rightsName
     * @param rightsType
     * @param firstLevelRes
     * @param mobile
     * @param couponReq
     * @return
     * @throws TxnException
     */
    private List<TlCouponDetail> generateCouponDetail(List<CmCustomerRightInfo> cmCustomerRightInfoList,
                                                      String rightsName, String rightsType, CustomerFirstLevelRes firstLevelRes, String mobile, CouponReq couponReq)
            throws TxnException {
        List<TlCouponDetail> tlCouponDetailList = new ArrayList<>(16);
        for (CmCustomerRightInfo cmCustomerRightInfo : cmCustomerRightInfoList) {
            TlCouponDetail detail = new TlCouponDetail();
            try {
                detail.setId(iSegmentService.getId(CustomerConstant.TL_COUPON_DETAIL_INFO));
            } catch (SegmentException e) {
                logger.error("主键生成错误", e);
                throw new TxnException(ErrorMsgConstant.ERROR_ID);
            }
            detail.setTenantId(cmCustomerRightInfo.getTenantId());
            detail.setOrganizationId(cmCustomerRightInfo.getOrganizationId());
            detail.setChannel(cmCustomerRightInfo.getChannel());
            detail.setCustomerId(cmCustomerRightInfo.getCustomerId());
            detail.setChineseName(firstLevelRes.getChineseName());
            detail.setIdNumber(firstLevelRes.getIdNumber());
            detail.setMobile(mobile);
            detail.setRightsId(cmCustomerRightInfo.getRightsId());
            detail.setRightsName(cmCustomerRightInfo.getRightsName());
            detail.setRightsType(rightsType);
            detail.setCustomerRightsId(cmCustomerRightInfo.getId());
            detail.setUpdateBy(cmCustomerRightInfo.getUpdateBy());
            detail.setRecordVersionNumber(cmCustomerRightInfo.getRecordVersionNumber());
            detail.setActivityId(couponReq.getActivityId());
            detail.setCouponSummaryId(0L);
            tlCouponDetailList.add(detail);
        }

        return tlCouponDetailList;
    }

    /**
     * 生成 新增客户权益对象
     *
     * @param couponReq
     * @param customerId
     * @param rightsQuantity 权益数量
     * @return
     * @throws TxnBizException
     */
    private CmCustomerRightInfo generateCmCustomerRightInfo(CouponReq couponReq, String customerId, int rightsQuantity)
            throws TxnBizException {
        CmCustomerRightInfo cmCustomerRightInfo = new CmCustomerRightInfo();
        try {
            cmCustomerRightInfo.setId(iSegmentService.getId(CustomerConstant.CM_CUSTOMER_RIGHT_INFO));
        } catch (SegmentException segmentException) {
            throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
        }
        cmCustomerRightInfo.setChannel(couponReq.getChannel());
        cmCustomerRightInfo.setCustomerId(customerId);
        cmCustomerRightInfo.setTenantId(couponReq.getTenantId());
        cmCustomerRightInfo.setLoanId("");
        cmCustomerRightInfo.setRightsStatus(CustomerConstant.RIGHTS_STATUS_0);
        cmCustomerRightInfo.setUpdateBy(customerId);
        cmCustomerRightInfo.setRecordVersionNumber(Constant.VERSION_NUMBER);
        cmCustomerRightInfo.setActivityId(couponReq.getActivityId());
        cmCustomerRightInfo.setRightsQuantity(1);
        cmCustomerRightInfo.setProductCode(couponReq.getProductCode());
        cmCustomerRightInfo.setCreateTime(new Date());
        cmCustomerRightInfo.setUpdateTime(new Date());

        return cmCustomerRightInfo;
    }

    /**
     * 逻辑： 1、基本校验 2、下单，判断下单状态，（保存下单返回记录） 3、下单成功，创建客户权益信息表，否则不新增客户权益信息记录 兑换权益资格
     *
     * 1.未兑换过权益时，cm_customer_rights_info表中的rights_id为空、rights_exchange_log_id为空、rights_status = 0；
     * 2.根据cm_customer_rights_info.id、customerId、tl_rights_exchange_log.id来生成唯一的场景订单号sceneOrderId；
     * 3.下单接口响应说明
     *   3.1.响应正常，code为0，不支持同一场景订单号的重复调用。
     *      3.1.1.处理中，orderState为0，订单的具体充值情况由批次任务进行轮询确认。
     *      3.1.2.成功，orderState为2，订单终态。
     *      3.1.3.失败，orderState为3，订单终态，调用方可生成新的sceneOrderId下单其他权益。
     *   3.2.响应异常，code不为0，表示本次下单失败，允许同一场景订单号重复调用直至响应正常。（权益平台会控制不会出现超发的情况）
     *   3.3.超时，支持同一sceneOrderId幂等请求
     * @param couponReq
     */
    @Override
    public CmCustomerRightInfo exchangeQualification(CouponReq couponReq) throws TxnBizException {
        // 参数校验
        if (couponReq.getId() == null) {
            logger.warn("权益资格CustomerRightInfo.id不能为空，customerId：{}", couponReq.getCustomerId());
            throw new TxnBizException(TxnRespCode.NOT_EMPTY);
        }

        if (StringUtils.isBlank(couponReq.getRightsId()) || StringUtils.isBlank(couponReq.getRightsQualificationId())) {
            logger.warn("权益id、权益资格id不能为空");
            throw new TxnBizException(TxnRespCode.NOT_EMPTY);
        }

        final long lockWaitTime = 5L;
        final long lockLeaseTime = CustomerConstant.REDIS_LOCK_LESS_TIME;

        // 缓存请求状态快速失败，避免短时间内重复大量的请求
        final String EXG_USER_KEY = String.format(REDIS_EXCHANGE_USER_KEY, couponReq.getUserId());
        final String thresholdTips = "处理中，请勿频繁提交";
        Boolean threshold = stringRedisTemplate.opsForValue().setIfAbsent(EXG_USER_KEY, couponReq.getUserId());
        if (threshold != null && !threshold) {
            throw new TxnBizException(thresholdTips);
        }
        stringRedisTemplate.expire(EXG_USER_KEY, lockLeaseTime, TimeUnit.SECONDS);

        // 权益库存锁和用户操作锁
        RLock rightsLock = null;
        RLock customerLock = null;

        try {
            Long cmCustomerRightInfoId = couponReq.getId();
            String userId = couponReq.getUserId();
            String channel = couponReq.getChannel();
            String tenantId = couponReq.getTenantId();
            String rightsId = couponReq.getRightsId();
            String productCode = couponReq.getProductCode();
            String rightsQualificationId = couponReq.getRightsQualificationId();
            Integer buyQuantity = couponReq.getBuyQuantity();

            boolean rightsLockFlag = false;
            boolean customerLockFlag = false;

            rightsLock = redissonLockService.getRLock(String.format(this.COUPON_EXCHANGE_LOCK_RIGHTS_KEY, rightsId));
            rightsLockFlag = rightsLock.tryLock(lockWaitTime, lockLeaseTime, TimeUnit.SECONDS);
            logger.info("权益库存锁定标识：{}，权益ID：{}", rightsLockFlag, rightsId);

            if (!rightsLockFlag) {
                throw new TxnBizException(thresholdTips);
            }

            // 业务校验
            PrRightsQualification rightsQualification = prRightsQualificationService.getByTenantIdAndQualificationId(couponReq.getTenantId(), couponReq.getRightsQualificationId());
            if (rightsQualification == null) {
                logger.info("权益类记录不存在，PrRightsQualification");
                throw new TxnBizException(TxnRespCode.NO_DATA);
            }

            // 查询并校验权益基本信息
            PrRightsAndInterests rightsAndInterests = prRightsAndInterestsService.checkRightsAndInterests(tenantId, rightsId, productCode);
            if (rightsAndInterests.getStoreQuantity() < buyQuantity) {
                logger.info("权益库存不足，rights_id：{}", rightsAndInterests.getRightsId());
                throw new TxnBizException(TxnRespCode.RIGHTS_EXCHANGE_OVER_DEFICIENT);
            }

            customerLock = redissonLockService.getRLock(String.format(this.COUPON_EXCHANGE_LOCK_USER_KEY, userId));
            customerLockFlag = customerLock.tryLock(lockWaitTime, lockLeaseTime, TimeUnit.SECONDS);
            logger.info("用户操作锁定标识：{}，UserId：{}", customerLockFlag, userId);

            if (!customerLockFlag) {
                throw new TxnBizException(thresholdTips);
            }

            // 获取客户一级、二级信息记录
            CustomerRes customerRes = customerService.getCustomerByChannelAndUserId(channel, userId, tenantId);
            if (customerRes == null || customerRes.getFirstLevel() == null || customerRes.getSecondLevel() == null) {
                logger.info("权益兑换，客户信息不存在");
                throw new TxnBizException("客户信息不存在");
            }

            final String customerId = customerRes.getFirstLevel().getCustomerId();

            // 查询客户权益信息表CmCustomerRightInfo，是否拥有此兑换资格，并且状态应为RIGHTS_STATUS_UNUSED
            Optional<CustRightRes> optionalUnusedRight = customerRightService.listByCustomerRightsType(userId, tenantId, channel, Collections.singletonList(RightsTypeEnum.QUALIFICATION.getCode())).stream()
                    .filter(item -> item.getRightsQualificationId().contains(rightsQualificationId)
                            && ObjectUtils.nullSafeEquals(cmCustomerRightInfoId, item.getId())
                            && CouponConstant.RIGHTS_STATUS_UNUSED.equals(item.getRightsStatus()))
                    .findFirst();

            if (!optionalUnusedRight.isPresent()) {
                logger.info("没有可兑换的权益资格，customerId：{}", customerId);
                throw new TxnBizException(TxnRespCode.QUALIFICATION_NOT_GRANTED);
            } else {
                CustRightRes custRightRes = optionalUnusedRight.get();

                if (custRightRes.getRightsQuantity() < buyQuantity) {
                    logger.info("权益资格数量不足以兑换，权益资格名称：{}，CustomerId：{}，UserId：{}", rightsQualification.getName(), customerId, userId);
                    throw new TxnBizException(TxnRespCode.QUALIFICATION_NOT_GRANTED);
                }

                String nowStr = DateUtils.format(new Date(), "yyyy-MM-dd");
                String effectStr = DateUtils.format(custRightRes.getEffectDate(), "yyyy-MM-dd");
                String expireStr = DateUtils.format(custRightRes.getExpiresDate(), "yyyy-MM-dd");
                boolean valid = nowStr.compareTo(effectStr) >= 0 && expireStr.compareTo(nowStr) >= 0;
                if (!valid) {
                    logger.info("权益兑换资格已失效");
                    throw new TxnBizException("权益资格已过期，请确认");
                }
            }

            CmCustomerRightInfo customerRightInfo = customerRightService.findRightInfoById(cmCustomerRightInfoId, customerId, tenantId);
            if (customerRightInfo == null) {
                logger.warn("资格记录未找到，资格记录id：{}", cmCustomerRightInfoId);
                throw new TxnBizException("未找到权益资格记录");
            }

            TlRightsExchangeLog exchangeLog = null;

            if (customerRightInfo.getRightsExchangeLogId() != null) {
                // 若关联的兑换记录存在，状态验证之后，若本次兑换选择与上次一样，则不变更；若选择不一样，则仅允许卡券类允许变更，其他不允许变更
                TlRightsExchangeLog lastExchangeLog = rightsExchangeLogService.findByIdAndCustomer(customerRightInfo.getRightsExchangeLogId(), customerId, tenantId, channel);
                if (lastExchangeLog == null) {
                    throw new TxnBizException("兑换状态异常，请稍后再试");
                }

                //是否支持兑换选择变更
                if (Objects.equals(lastExchangeLog.getOrderState(), CouponConstant.EXCHANGE_COUPON_ORDER_ERROR)) {
                    if (Objects.equals(CouponConstant.EXCHANGE_RIGHTS_CATE_KA_MI, lastExchangeLog.getOrderType())) {
                        if (Objects.equals(lastExchangeLog.getRightsId(), rightsAndInterests.getRightsId())) {
                            exchangeLog = lastExchangeLog;
                        } else {
                            exchangeLog = null;
                        }
                    } else {
                        exchangeLog = lastExchangeLog;
                    }
                } else {
                    exchangeLog = lastExchangeLog;
                }

                boolean couldChooseAnotherRights = Objects.equals(lastExchangeLog.getOrderState(), CouponConstant.EXCHANGE_COUPON_ORDER_ERROR)
                        && Objects.equals(CouponConstant.EXCHANGE_RIGHTS_CATE_KA_MI, lastExchangeLog.getOrderType())
                        && !Objects.equals(lastExchangeLog.getRightsId(), rightsAndInterests.getRightsId());

                if (couldChooseAnotherRights) {
                    logger.info("权益兑换，最近一次兑换异常，但权益为卡密类型，允许再次选择其他权益进行兑换");
                    exchangeLog = null;
                } else {
                    exchangeLog = lastExchangeLog;

                    // 校验最终权益
                    PrRightsAndInterests finalRights = prRightsAndInterestsService.checkRightsAndInterests(tenantId, exchangeLog.getRightsId(), productCode);
                    if (finalRights.getStoreQuantity() < buyQuantity) {
                        logger.info("权益库存不足，rights_id：{}", finalRights.getRightsId());
                        throw new TxnBizException(TxnRespCode.RIGHTS_EXCHANGE_OVER_DEFICIENT);
                    }
                }
            }

            if (exchangeLog == null) {
                //该处应使用对应TlRightsExchangeLog的主键生成ID，但已错误使用，只能继续延用，避免随意变更后的ID冲突
                Long exchangeLogId = iSegmentService.getId(CustomerConstant.TL_COUPON_DETAIL_INFO);
                String sceneOrderId = DateUtils.format(new Date(), "yyyyMMddHHmmss") + exchangeLogId + "";
                exchangeLog = this.initExchangeLogForPlatform(customerRes, couponReq, rightsAndInterests, sceneOrderId, exchangeLogId);

                // 将rightsExchangeLogId保存到customerRightInfo中，在请求超时情况下，可保证场景订单编号sceneOrderId不变
                customerRightService.updateRightExchangeId(customerRightInfo, exchangeLogId);
            }

            if (!StringUtils.isEmpty(exchangeLog.getCouponSystemOrderId())) {
                throw new TxnBizException("已完成兑换，请查询订单状态");
            }

            // step2：下单 - 资格兑换，支持幂等请求
            CouponRequest05 request05 = generateCreateCouponReq(exchangeLog, customerRes.getSecondLevel().getMobile(), exchangeLog.getSceneOrderId());

            CodeTableTypeRes outSwitch = iCodeTableTypeService.findByTypeIdAndTenId(EXCHANGE_OUT_RIGHT_SWITCH, couponReq.getTenantId());

            CouponResponse05 couponResponse = new CouponResponse05();
            //默认请求外部权益平台
            if (Objects.isNull(outSwitch) || "1".equals(outSwitch.getIsvalid())) {
                logger.info("发送权益平台兑换请求");
                Optional<CouponResponse05> optional = this.cardCouponCreateOrder(request05);
                if (!optional.isPresent()) {
                    updateExchangeLogForPlatform(exchangeLog, null);
                    throw new TxnBizException("兑换失败，请重试");
                }

                //业务请求-响应成功处理
                couponResponse = optional.get();
            } else {
                logger.info("内部卡券系统兑换请求");
                PrCardRollDetailInfo rightsPassExtractReq = new PrCardRollDetailInfo();
                BeanUtils.copyProperties(request05, rightsPassExtractReq);
                //此处场景id为场景订单id
                rightsPassExtractReq.setSceneId(exchangeLog.getSceneOrderId());
                rightsPassExtractReq.setCardRollId(request05.getCouponId());
                rightsPassExtractReq.setReceivePhone(request05.getChargeAccount());
                rightsPassExtractReq.setTenantId(couponReq.getTenantId());

                //提取卡密
                PrCardRollDetailInfo prCardRollDetailInfo = iCardRollService.rightsPassExtract(rightsPassExtractReq);

                couponResponse.setId(String.valueOf(prCardRollDetailInfo.getId()));
                couponResponse.setOrderState(CouponConstant.EXCHANGE_COUPON_ORDER_SUCCESS);
                couponResponse.setOrderCreateTime(DateUtils.format(new Date(), DateUtils.DATE_PATTERN));

                Card card = new Card();
                card.setCardNo(prCardRollDetailInfo.getCardNumber());
                card.setCardPassword(prCardRollDetailInfo.getPassWord());
                List<Card> cardList = new ArrayList<>();
                cardList.add(card);

                couponResponse.setCards(cardList);
            }

            try {
                updateExchangeLogForPlatform(exchangeLog, couponResponse);
            } catch (Exception e) {
                logger.error("权益兑换，记录状态更新异常，记录ID：{}, 响应: {}", exchangeLog.getId(), JSON.toJSONString(couponResponse));
                updateExchangeLogException(exchangeLog);

                throw new TxnBizException("兑换失败，建议选兑其他权益");
            }

            final String orderState = exchangeLog.getOrderState();
            if (CouponConstant.EXCHANGE_COUPON_ORDER_SUCCESS.equals(orderState)
                    || CouponConstant.EXCHANGE_COUPON_ORDER_PROCESSING.equals(orderState)) {
                // 兑换成功或处理中，更新兑换并扣减库存
                this.exchangeCustomerRightUsed(exchangeLog, customerRightInfo);
            } else if (CouponConstant.EXCHANGE_COUPON_ORDER_FAIL.equals(orderState)) {
                logger.warn("下单失败，场景订单编号：{}", exchangeLog.getSceneOrderId());

                // 重置权益资格（rights_exchange_log_id赋空），允许选择其他权益
                this.resetCustomerRightUnused(customerRightInfo);
                throw new TxnBizException("兑换失败，建议选择其他权益");
            } else {
                logger.warn("未知的权益下单响应状态：{}， 场景订单编号：{}", orderState, exchangeLog.getSceneOrderId());

                // 异常状况，锁定客户资格并扣减库存，待人工介入排查
                this.exchangeCustomerRightUsed(exchangeLog, customerRightInfo);
                throw new TxnBizException("兑换异常，稍后请联系客服");
            }

            return customerRightInfo;
        } catch (TxnBizException e) {
            //特定库存不足的提示语修改
            if (Integer.valueOf(3311).equals(e.getErrCode())) {
                throw new TxnBizException(TxnRespCode.RIGHTS_EXCHANGE_OVER_DEFICIENT);
            }
            throw e;
        } catch (Exception e) {
            logger.error("权益兑换，处理异常", e);
            throw new TxnBizException("兑换失败，请稍后再试");
        } finally {
            // 按照try逻辑块的加锁顺序，逆序解锁
            if (customerLock != null && customerLock.isLocked() && customerLock.isHeldByCurrentThread()) {
                customerLock.unlock();
            }

            if (rightsLock != null && rightsLock.isLocked() && rightsLock.isHeldByCurrentThread()) {
                rightsLock.unlock();
            }

            // 删除缓存
            stringRedisTemplate.delete(String.format(EXG_USER_KEY, couponReq.getUserId()));
            logger.info("锁标识清除完毕");
        }
    }

    /**
     * 权益平台下单
     *
     * @param req
     * @return
     */
    @Override
    public Optional<CouponResponse05> cardCouponCreateOrder(CouponRequest05 req) {
        String reqJson = JSON.toJSONString(req);

        ApiResponse<CouponResponse05> couponResponse05 = outerGatewayService.order(reqJson);
        if (couponResponse05 == null || couponResponse05.getData() == null) {
            logger.error("权益兑换异常，请求报文: {}", JSON.toJSONString(req));

            return Optional.empty();
        }

        logger.info("权益兑换，请求：{}，响应：{}", reqJson, JSON.toJSONString(couponResponse05.getData()));
        return Optional.ofNullable(couponResponse05.getData());
    }

    @Override
    public CardCouponDetailRes cardCouponDetail(CouponReq couponReq) throws TxnException {
        CouponRequest06 couponRequest06 = new CouponRequest06(couponReq.getSceneOrderId(), couponReq.getPlatOrderId());

        CardCouponDetailRes res = new CardCouponDetailRes();
        CodeTableTypeRes outSwitch = iCodeTableTypeService.findByTypeIdAndTenId(EXCHANGE_OUT_RIGHT_SWITCH, couponReq.getTenantId());
        //默认请求外部权益平台
        if (Objects.isNull(outSwitch) || "1".equals(outSwitch.getIsvalid())) {
            logger.info("通过权益平台查询卡券详情信息请求");
            ApiResponse<CouponResponse06> couponResponse06ApiResponse = outerGatewayService.cardCouponDetail(JSON.toJSONString(couponRequest06));
            if (TxnRespCode.SUCCESS.getCode() != couponResponse06ApiResponse.getCode()) {
                throw new TxnBizException(couponResponse06ApiResponse.getCode(), couponResponse06ApiResponse.getMessage());
            }

            CouponResponse06 data = couponResponse06ApiResponse.getData();
            BeanUtils.copyProperties(data, res);
        } else {
            logger.info("通过内部系统查询卡券详情信息请求");
            CustomerRes customerRes = customerService.getCustomerByChannelAndUserId(couponReq.getChannel(),
                    couponReq.getUserId(), couponReq.getTenantId());
            if (customerRes == null || customerRes.getFirstLevel() == null || customerRes.getSecondLevel() == null) {
                throw new TxnBizException("客户信息不存在");
            }

            TlRightsExchangeLog rightsExchangeLog = rightsExchangeLogService.findBySceneOrderIdAndPlatOrderId(couponReq.getTenantId(),
                    customerRes.getFirstLevel().getCustomerId(), couponReq.getSceneOrderId(), couponReq.getPlatOrderId());
            if (rightsExchangeLog == null) {
                logger.error("查无该权益兑换信息，userId：{}，platOrderId：{}", couponReq.getUserId(), couponReq.getPlatOrderId());
                throw new TxnBizException("无法查询到该订单");
            }

            res.setCardNo(rightsExchangeLog.getCardNo());
            res.setCardPassword(rightsExchangeLog.getCardPassword());
            res.setOrderState(rightsExchangeLog.getOrderState());
            res.setSceneOrderId(rightsExchangeLog.getSceneOrderId());
        }
        return res;
    }

    /**
     * 是否允许选择其他权益进行兑换
     *
     * @param lastExchangeLog
     * @param chosenRightsId
     * @return
     */
    @Override
    public boolean couldChooseAnotherRights(TlRightsExchangeLog lastExchangeLog, String chosenRightsId) {
        if (lastExchangeLog == null) {
            return true;
        }

        if (Objects.equals(chosenRightsId, lastExchangeLog.getRightsId())) {
            return true;
        }

        if (Objects.equals(lastExchangeLog.getOrderState(), CouponConstant.EXCHANGE_COUPON_ORDER_FAIL)) {
            logger.info("权益兑换，最近一次兑换明确失败，可重新兑换");
            return true;
        }

        if (Objects.equals(lastExchangeLog.getOrderState(), CouponConstant.EXCHANGE_COUPON_ORDER_ERROR)
                && Objects.equals(CouponConstant.EXCHANGE_RIGHTS_CATE_KA_MI, lastExchangeLog.getOrderType())) {
            logger.info("权益兑换，最近一次兑换异常，但权益为卡密类型，允许再次选择其他权益进行兑换");
            return true;
        }

        logger.info("权益兑换，最近一次兑换状态未通过验证，不允许再次选择其他权益进行兑换");
        return false;
    }

    /**
     * 生成 2.5 下单请求字符串
     *
     * @param exchangeLog
     * @param mobile
     * @return
     */
    private CouponRequest05 generateCreateCouponReq(TlRightsExchangeLog exchangeLog, String mobile, String sceneOrderId) {
        CouponRequest05 request = new CouponRequest05();
        //下单手机号
        request.setMobile(mobile);
        request.setFingerprint(Md5Utils.hash(mobile));
        request.setCouponId(exchangeLog.getRightsId());
        request.setBuyQuantity(exchangeLog.getBuyQuantity().intValue());
        //充值手机号
        request.setChargeAccount(exchangeLog.getChargeAccount());
        request.setSceneOrderId(sceneOrderId);
        return request;
    }

    private String generateCreateOderReq(PrActivityInfo activityInfo, String mobile, String sceneOrderId) {
        CouponRequest05 request = new CouponRequest05();
        request.setMobile(mobile);
        request.setFingerprint(Md5Utils.hash(mobile));
        request.setCouponId(activityInfo.getRightsId());
        request.setBuyQuantity(activityInfo.getRightsQuantity());
        request.setChargeAccount(mobile);
        request.setSceneOrderId(sceneOrderId);
        return JSON.toJSONString(request);

    }

    /**
     * 初始化一条权益平台调用记录
     * @param customerRes
     * @param couponReq
     * @param rightsAndInterests
     * @return
     * @throws TxnBizException
     */
    protected TlRightsExchangeLog initExchangeLogForPlatform(CustomerRes customerRes, CouponReq couponReq, PrRightsAndInterests rightsAndInterests, String sceneOrderId, Long exchangeLogId) throws TxnBizException {
        TlRightsExchangeLog rightsExchangeLog = new TlRightsExchangeLog();
        rightsExchangeLog.setId(exchangeLogId);
        final String customerId = customerRes.getFirstLevel().getCustomerId();

        // 兑换基本信息
        rightsExchangeLog.setTenantId(customerRes.getSecondLevel().getTenantId());
        rightsExchangeLog.setChannel(couponReq.getChannel());
        rightsExchangeLog.setCustomerId(customerId);
        rightsExchangeLog.setChineseName(customerRes.getFirstLevel().getChineseName());
        rightsExchangeLog.setRightsId(couponReq.getRightsId());
        rightsExchangeLog.setRightsType(rightsAndInterests.getRightsType());
        rightsExchangeLog.setQualificationId(couponReq.getRightsQualificationId());
        rightsExchangeLog.setBuyQuantity(couponReq.getBuyQuantity().shortValue());
        rightsExchangeLog.setCouponName(rightsAndInterests.getRightsName());
        rightsExchangeLog.setLogoUrl(rightsAndInterests.getLogoUrl());
        rightsExchangeLog.setOrderType(rightsAndInterests.getRightsCate());

        // 权益平台初始化值
        rightsExchangeLog.setSceneId(CustomerConstant.SCENE_ID);// 场景ID
        rightsExchangeLog.setSceneOrderId(sceneOrderId);
        rightsExchangeLog.setCustomerPhone(customerRes.getSecondLevel().getMobile());// 客户手机号
        rightsExchangeLog.setChargeAccount(couponReq.getChargeAccount());// 充值账号

        // 其他信息
        rightsExchangeLog.setAppId("");
        rightsExchangeLog.setCreateTime(new Date());
        rightsExchangeLog.setCreateBy(customerId);
        rightsExchangeLog.setRecordVersionNumber(Constant.VERSION_NUMBER);

        rightsExchangeLogService.save(rightsExchangeLog);
        return rightsExchangeLog;
    }

    /**
     * 根据权益平台响应结果，更新一条现存记录
     * @param rightsExchangeLog
     * @param response05
     * @return
     */
    protected TlRightsExchangeLog updateExchangeLogForPlatform(TlRightsExchangeLog rightsExchangeLog, CouponResponse05 response05) {
        if (response05 == null) {
            rightsExchangeLog.setOrderState(CouponConstant.EXCHANGE_COUPON_ORDER_ERROR);
        } else {
            rightsExchangeLog.setCouponSystemOrderId(response05.getId());
            rightsExchangeLog.setFingerprint(response05.getCustomerFingerprint());

            rightsExchangeLog.setUnitPrice(response05.getUnitPrice());
            rightsExchangeLog.setTotalPrice(response05.getTotalPrice());
            rightsExchangeLog.setOrderType(response05.getOrderType());
            rightsExchangeLog.setCustomerFingerprint(response05.getCustomerFingerprint());

            rightsExchangeLog.setResJson(JSON.toJSONString(response05));
            rightsExchangeLog.setOrderCreateTime(DateUtils.parseDate(response05.getOrderCreateTime()));
            rightsExchangeLog.setOrderCompleteTime(new Date());

            // 订单状态
            rightsExchangeLog.setOrderState(response05.getOrderState());

            boolean success = Objects.equals(CouponConstant.EXCHANGE_COUPON_ORDER_SUCCESS, response05.getOrderState())
                    || Objects.equals(CouponConstant.EXCHANGE_COUPON_ORDER_PROCESSING, response05.getOrderState());

            if (success && !CollectionUtils.isEmpty(response05.getCards())) {
                Card card = response05.getCards().get(0);
                rightsExchangeLog.setCardPassword(card.getCardPassword());
                rightsExchangeLog.setCardNo(card.getCardNo());
                if (!org.springframework.util.StringUtils.isEmpty(card.getCardPassword()) && card.getCardPassword().length() > 4) {
                    //对权益平台生成的卡券特殊处理
                    if (Constants.DEFAULT_CARD_PASSWORD_PREFIX.equalsIgnoreCase(card.getCardPassword().substring(0, 4))) {
                        if (StringUtils.isNotBlank(card.getQrCodeLink())) {
                            rightsExchangeLog.setCardPassword(card.getQrCodeLink());
                        }
                    }
                }
            }
        }

        rightsExchangeLogService.update(rightsExchangeLog);
        return rightsExchangeLog;
    }

    /**
     * 更新权益兑换记录状态为失败
     * @param rightsExchangeLog
     */
    protected void updateExchangeLogException(TlRightsExchangeLog rightsExchangeLog) {
        TlRightsExchangeLog updating = new TlRightsExchangeLog();
        updating.setId(rightsExchangeLog.getId());
        updating.setTenantId(rightsExchangeLog.getTenantId());
        updating.setChannel(rightsExchangeLog.getChannel());

        //重要的逻辑字段赋值
        updating.setOrderState(CouponConstant.EXCHANGE_COUPON_ORDER_ERROR);
        updating.setCardNo("");
        updating.setCardPassword("");

        rightsExchangeLogService.save(updating);
    }

    /**
     * 兑换权益，更新客户的权益资格状态为已使用
     * @param rightsExchangeLog
     * @param rightInfo
     * @return
     */
    protected void exchangeCustomerRightUsed(TlRightsExchangeLog rightsExchangeLog, CmCustomerRightInfo rightInfo) {
        // 状态设置
        customerRightService.updateRightToUsed(rightInfo, rightsExchangeLog.getRightsId());
        // 更新库存
        prRightsAndInterestsService.exchangeRightsInterests(rightsExchangeLog.getTenantId(), rightsExchangeLog.getRightsId(), rightsExchangeLog.getBuyQuantity());
    }

    /**
     * 撤销客户的权益资格兑换状态为未使用
     * @param info
     * @return
     */
    private void resetCustomerRightUnused(CmCustomerRightInfo info) {
        customerRightService.resetRightToUnused(info);
    }

    /**
     * // 下单失败处理逻辑 保存权益下发明细表
     */
    private void saveRightsExchangeLogOnCreatingFail(CouponResponse05 response05, CouponReq couponReq,
                                                     CustomerRes customerRes, PrRightsAndInterests rightsAndInterests) throws TxnBizException {
        CustomerFirstLevelRes firstLevelRes = customerRes.getFirstLevel();
        TlRightsExchangeLog tlRightsExchangeLog = new TlRightsExchangeLog();
        tlRightsExchangeLog.setTenantId(couponReq.getTenantId());
        tlRightsExchangeLog.setCustomerId(firstLevelRes.getCustomerId());
        tlRightsExchangeLog.setChineseName(firstLevelRes.getChineseName());
        tlRightsExchangeLog.setQualificationId(couponReq.getRightsQualificationId());
        tlRightsExchangeLog.setRightsId(rightsAndInterests.getRightsId());
        tlRightsExchangeLog.setRightsType(rightsAndInterests.getRightsType());
        tlRightsExchangeLog.setSceneId(CustomerConstant.SCENE_ID);
        tlRightsExchangeLog.setCreateTime(new Date());
        tlRightsExchangeLog.setCreateBy("");
        tlRightsExchangeLog.setResJson(JSON.toJSONString(response05));
        tlRightsExchangeLog.setRecordVersionNumber(Constant.VERSION_NUMBER);

        try {
            tlRightsExchangeLog.setId(iSegmentService.getId(CustomerConstant.TL_COUPON_DETAIL_INFO));
        } catch (SegmentException e) {
            throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
        }
        rightsExchangeLogService.save(tlRightsExchangeLog);
    }

    /**
     * 下单成功触发下单逻辑
     */
    private TlRightsExchangeLog saveRightsExchangeLogOnCreatingSuccess(CouponResponse05 response05, CouponReq couponReq,
                                                                       CustomerRes customerRes, PrRightsAndInterests rightsAndInterests, String sceneOrderId) throws TxnBizException {
        CustomerFirstLevelRes firstLevelRes = customerRes.getFirstLevel();
        TlRightsExchangeLog rightsExchangeLog = new TlRightsExchangeLog();

        try {
            rightsExchangeLog.setId(iSegmentService.getId(CustomerConstant.TL_COUPON_DETAIL_INFO));
        } catch (SegmentException e) {
            throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
        }
        rightsExchangeLog.setTenantId(couponReq.getTenantId());
        rightsExchangeLog.setCustomerId(firstLevelRes.getCustomerId());
        rightsExchangeLog.setChineseName(firstLevelRes.getChineseName());
        rightsExchangeLog.setRightsId(rightsAndInterests.getRightsId());
        rightsExchangeLog.setRightsType(rightsAndInterests.getRightsType());
        rightsExchangeLog.setSceneId(CustomerConstant.SCENE_ID);
        rightsExchangeLog.setSceneOrderId(sceneOrderId);
        rightsExchangeLog.setCouponSystemOrderId(response05.getId());
        rightsExchangeLog.setFingerprint(response05.getCustomerFingerprint());
        rightsExchangeLog.setBuyQuantity(couponReq.getBuyQuantity().shortValue());
        rightsExchangeLog.setCouponName(rightsAndInterests.getRightsName());
        rightsExchangeLog.setUnitPrice(response05.getUnitPrice());
        rightsExchangeLog.setTotalPrice(response05.getTotalPrice());
        rightsExchangeLog.setOrderType(response05.getOrderType());
        rightsExchangeLog.setCustomerFingerprint(response05.getCustomerFingerprint());
        rightsExchangeLog.setCustomerPhone(response05.getCustomerPhone());
        rightsExchangeLog.setChargeAccount(couponReq.getChargeAccount());
        rightsExchangeLog.setOrderState(response05.getOrderState());
        rightsExchangeLog.setOrderCreateTime(DateUtils.parseDate(response05.getOrderCreateTime()));
        rightsExchangeLog.setCreateTime(new Date());
        rightsExchangeLog.setCreateBy(Constants.DEFAULT_USER);
        rightsExchangeLog.setAppId("");
        rightsExchangeLog.setChannel(couponReq.getChannel());
        rightsExchangeLog.setLogoUrl(rightsAndInterests.getLogoUrl());
        rightsExchangeLog.setResJson(JSON.toJSONString(response05));
        rightsExchangeLog.setQualificationId(couponReq.getRightsQualificationId());
        rightsExchangeLog.setRecordVersionNumber(Constant.VERSION_NUMBER);

        List<Card> cardList = response05.getCards();
        if (cardList != null && cardList.size() > 0) {
            Card card = cardList.get(0);
            rightsExchangeLog.setCardPassword(card.getCardPassword());
            rightsExchangeLog.setCardNo(card.getCardNo());
            if (!org.springframework.util.StringUtils.isEmpty(card.getCardPassword()) && card.getCardPassword().length() > 4) {
                //对权益平台生成的卡券特殊处理
                if (Constants.DEFAULT_CARD_PASSWORD_PREFIX.equalsIgnoreCase(card.getCardPassword().substring(0, 4))) {
                    if (StringUtils.isNotBlank(card.getQrCodeLink())) {
                        rightsExchangeLog.setCardPassword(card.getQrCodeLink());
                    }
                }
            }
        }

        rightsExchangeLogService.save(rightsExchangeLog);
        return rightsExchangeLog;
    }

    private TlRightsExchangeLog saveRightsExchangeLogByYd(SendYdCouponReq sendYdCouponReq, CustomerRes customerRes) throws TxnBizException {
        CustomerFirstLevelRes firstLevelRes = customerRes.getFirstLevel();
        TlRightsExchangeLog rightsExchangeLog = new TlRightsExchangeLog();

        try {
            rightsExchangeLog.setId(iSegmentService.getId(CustomerConstant.TL_COUPON_DETAIL_INFO));
        } catch (SegmentException e) {
            throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
        }

        rightsExchangeLog.setTenantId(sendYdCouponReq.getTenantId());
        rightsExchangeLog.setCustomerId(firstLevelRes.getCustomerId());
        rightsExchangeLog.setChineseName(firstLevelRes.getChineseName());
        rightsExchangeLog.setRightsId(sendYdCouponReq.getProductId());
        rightsExchangeLog.setRightsType(CouponConstant.ACTIVITY_TYPE_THIRD_COUPON);
        rightsExchangeLog.setSceneId(CustomerConstant.SCENE_ID);
        rightsExchangeLog.setSceneOrderId("YD");
        rightsExchangeLog.setCouponName("易点平台优惠券");
        rightsExchangeLog.setUnitPrice(BigDecimal.ZERO);
        rightsExchangeLog.setTotalPrice(BigDecimal.ZERO);
        rightsExchangeLog.setOrderType("3");
        rightsExchangeLog.setCustomerPhone(sendYdCouponReq.getPhone());
        rightsExchangeLog.setChargeAccount(sendYdCouponReq.getPhone());
        rightsExchangeLog.setOrderState("4");
        rightsExchangeLog.setOrderCreateTime(DateUtils.parseDate(sendYdCouponReq.getActivateTime()));
        rightsExchangeLog.setCreateTime(DateUtils.parseDate(sendYdCouponReq.getActivateTime()));
        rightsExchangeLog.setCreateBy(Constants.DEFAULT_USER);
        rightsExchangeLog.setChannel(sendYdCouponReq.getChannelNo());
        rightsExchangeLog.setQualificationId("YD");
        rightsExchangeLog.setRecordVersionNumber(Constant.VERSION_NUMBER);

        rightsExchangeLogService.save(rightsExchangeLog);
        return rightsExchangeLog;
    }

    /**
     * 判断此活动权益是否已下发 customerId, activityId, rightsId, tenantId
     */
    private void checkActivityGrantedRights(String customerId, PrActivityInfo activityInfo, String rightsId,
                                            String tenantId) throws TxnBizException {
        //校验库存
        Integer storeQuantity = activityInfo.getStoreQuantity();
        if (storeQuantity == null || storeQuantity <= 0) {
            throw new TxnBizException(TxnRespCode.ACTIVITY_STOCK_DEFICIENT);
        }
        String startTime = "";
        String endTime = "";
        String cycle = activityInfo.getCycle();
        if (RightsCycleInActivityEnum.WEEKLY.getCode().equals(cycle)) {
            startTime = DateUtils.getDayOfWeek(Calendar.MONDAY) + " 00:00:00";
            endTime = DateUtils.getDayOfWeek(Calendar.SUNDAY) + " 23:59:59";
        }
        if (RightsCycleInActivityEnum.MONTHLY.getCode().equals(cycle)) {
            startTime = DateUtils.format(DateUtils.getMonthStartTime(new Date()), DateUtils.DATETIME_PATTERN);
            endTime = DateUtils.format(DateUtils.getMonthEndTime(new Date()), DateUtils.DATETIME_PATTERN);
        }
        if (RightsCycleInActivityEnum.ONCE.getCode().equals(cycle)) {
            startTime = null;
            endTime = null;
        }

        List<TlCouponDetail> couponDetails = tlCouponDetailService.findByCustomerIdAndActivityIdAndRightsIdWithPeriod(
                customerId, String.valueOf(activityInfo.getActivityId()), rightsId, tenantId, startTime, endTime);
        if (!couponDetails.isEmpty()) {
            logger.info("根据客户id:{}、租户号:{}、权益资格id:{}、活动号:{} 查询客户权益信息表，已领取权益，不能重复领取", customerId, tenantId, rightsId,
                    activityInfo.getActivityId());
            throw new TxnBizException(TxnRespCode.COUPON_IN_ACTIVITY_ALREADY_EXISTS);
        }
        //校验抢五折券成功次数
        Integer limitTime = Objects.isNull(activityInfo.getLimitTime()) ? 0 : activityInfo.getLimitTime();
        Integer limitTimes = Objects.isNull(activityInfo.getLimitTimes()) ? 0 : activityInfo.getLimitTimes();


        String limitStartTime = "";
        String limitEndTime = "";
        if (!RightsAndActivityLimitTimeEnum.LIMIT_TIME_DEFAULT.getCode().equals(String.valueOf(limitTime))) {
            switch (RightsAndActivityLimitTimeEnum.getEnumCode(String.valueOf(limitTime))) {
                case LIMIT_TIME_WEEK:
                    limitStartTime = DateUtils.getDayOfWeek(Calendar.MONDAY) + " 00:00:00";
                    limitEndTime = DateUtils.getDayOfWeek(Calendar.SUNDAY) + " 23:59:59";
                    break;
                case LIMIT_TIME_MONTH:
                    limitStartTime = DateUtils.format(DateUtils.getMonthStartTime(new Date()), DateUtils.DATE_PATTERN) + " 00：00：00";
                    limitEndTime = DateUtils.format(DateUtils.getMonthEndTime(new Date()), DateUtils.DATE_PATTERN) + " 23:59:59";
                    break;
                case LIMIT_TIME_QUARTER:
                    limitStartTime = DateUtils.getCurrentQuarterFirst() + " 00:00:00";
                    limitEndTime = DateUtils.getCurrentQuarterLast() + " 23:59:59";
                    break;
                case LIMIT_TIME_YEAR:
                    limitStartTime = DateUtils.getCurrentYearFirst() + " 00:00:00";
                    limitEndTime = DateUtils.getCurrentYearLast() + " 23:59:59";
                    break;
                default:
                    throw new TxnBizException(3135, "limit_time字段数值异常");
            }
            String activityId = String.valueOf(activityInfo.getActivityId());

            List<TlCouponDetail> couponInfos = tlCouponDetailService.findByCustomerIdAndActivityIdAndRightsIdWithPeriod(
                    customerId, activityId, rightsId, tenantId, limitStartTime, limitEndTime);
            if (couponInfos.size() >= limitTimes) {
                logger.info("根据客户id:{}、租户号:{}、权益资格id:{}、活动号:{} 查询客户权益信息表，已达到最大领取次数，不能再领取", customerId, tenantId, rightsId,
                        activityId);
                //达到最大抢券次数的客户，不直接提示失败原因
                throw new TxnBizException(TxnRespCode.TRY_LOCK_FAIL);
            }
        }

    }

    /**
     * 检查活动id,判断是否为空或者为虚活动（以V开头）
     *
     * @param activityId
     * @return
     */
    private Boolean checkActivityId(String activityId) {
        return StringUtils.isNotBlank(activityId) && !activityId.toLowerCase().startsWith("v");
    }


    public void setMonthslyTypeProp(int i, CmCustomerRightInfo cmCustomerRightInfo, List<CmCustomerRightInfo> cmCustomerRightInfoList) throws TxnBizException {
        Date initEffectiveDate = cmCustomerRightInfo.getEffectDate();
        Date initExpireDate = cmCustomerRightInfo.getExpiresDate();
        CmCustomerRightInfo tmp = new CmCustomerRightInfo();
        BeanUtils.copyProperties(cmCustomerRightInfo, tmp);
        // 设置连月的id、生效日期、失效日期
        if (i > 0) {
            try {
                tmp.setId(iSegmentService.getId(CustomerConstant.CM_CUSTOMER_RIGHT_INFO));
                tmp.setEffectDate(DateUtils.addMonth(initEffectiveDate, i));
                tmp.setExpiresDate(DateUtils.addMonth(initExpireDate, i));
            } catch (SegmentException e) {
                throw new TxnBizException(TxnRespCode.GET_SEGMENT_ID_ERROR);
            }
        }
        cmCustomerRightInfoList.add(tmp);
    }


    private void setcmCustomerRightIfoProp(CmCustomerRightInfo cmCustomerRightInfo, PrRightsAndInterests rightsAndInterests, String rightsId, String tenantId) throws TxnBizException {
        cmCustomerRightInfo.setRightsId(rightsId);
        cmCustomerRightInfo.setRightsName(rightsAndInterests.getRightsName());
        cmCustomerRightInfo.setCycleUse(rightsAndInterests.getCycleUse());
        cmCustomerRightInfo.setDiscountAmtLimit(rightsAndInterests.getDiscountAmtLimit());

        PrOrganizationTable org =
                organizationTableService.findByOrId(rightsAndInterests.getOrganizationId(), tenantId);
        cmCustomerRightInfo.setOrganizationId(org.getOrganizationId());

        cmCustomerRightInfo.setRightsType(rightsAndInterests.getRightsType());
        cmCustomerRightInfo.setAllowProducts(rightsAndInterests.getAllowProducts());
        cmCustomerRightInfo.setFaceValue(rightsAndInterests.getFaceValue());
        cmCustomerRightInfo.setThresholdPrice(rightsAndInterests.getThresholdPrice());
        cmCustomerRightInfo.setDiscountValue(rightsAndInterests.getDiscountValue());
        cmCustomerRightInfo.setDiscountsCeiling(rightsAndInterests.getDiscountsCeiling());
    }

    private void checkRightsStoreQuantity(Map<String, Integer> map, String tenantId) throws TxnBizException {
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            PrRightsAndInterests rightsAndInterests = prRightsAndInterestsService.getByRightsId(tenantId, entry.getKey());
            Integer storeQuantity = rightsAndInterests.getStoreQuantity();
            if (entry.getValue() > storeQuantity) {
                logger.info("活动需下发权益id:{}的数量为：{}超过了库存数量：{}", entry.getKey(), entry.getValue(), storeQuantity);
                throw new TxnBizException(TxnRespCode.RIGHTS_AND_INTEREST_STOCK_DEFICIENT);
            }
        }
    }

    private String setCouponType(String activityType, String type) {
        if (StringUtils.isNotBlank(activityType)) {
            if (activityType.equals(CouponConstant.ACTIVITY_TYPE_CASH_OFFERS)) {
                logger.info("---现金优惠类活动---");
                type = "0";
            } else if (activityType.equals(CouponConstant.ACTIVITY_TYPE_QUALIFICATION)) {
                logger.info("---权益资格类活动---");
                type = "1";
            } else if (activityType.equals(CouponConstant.ACTIVITY_TYPE_THIRD_COUPON)) {
                logger.info("---第三方平台下单类活动---");
                type = "2";
            } else if (activityType.equals(CouponConstant.ACTIVITY_TYPE_COUPON_BOSS)) {
                logger.info("---权益平台下单类活动---");
                type = "3";
            }
        }
        return type;
    }

}
